package com.video.netty.InChannelHandler;

import com.alibaba.fastjson.JSONObject;
import com.qf.entity.RunLogEntity;
import com.video.commons.runtimelog.RequestIdThreadLocal;
import com.video.commons.runtimelog.RunLogUtil;
import com.video.netty.commons.init.InitHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;

import java.util.Set;
import java.util.UUID;


@Order(150)
@BaseChannelHandler.MsgHandler
@ChannelHandler.Sharable
public class InitMsgChannelHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Autowired
    private InitHandler initHandler;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("#{'${msg.print.exclude}' == '' ? null : '${msg.print.exclude}'.split(',')}")
    private Set<String> excludes;

    @Value("#{'${msg.print.include}' == '' ? null : '${msg.print.include}'.split(',')}")
    private Set<String> includes;

    /**
     * 接收Netty入站消息
     * @param ctx
     * @param text
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame text) throws Exception {
        //将文本转换成json字符串
        String id = UUID.randomUUID().toString();
        //保存请求的id
        RequestIdThreadLocal.putId(id);
        //异常变量
        try {
            JSONObject jsonObject = JSONObject.parseObject(text.text());

            //TODO 记录接收消息的日志
            printMsgLog(jsonObject, id);

            //收到消息后直接交给后面的处理器处理
            ctx.fireChannelRead(jsonObject);
        } catch (Throwable t) {
            //将运行日志信息保存到日志存储栈中
            RunLogUtil.error("消息转换失败，格式不正确：" + text.text(), t);
        } finally {
            //请求结束后，清除id
            RequestIdThreadLocal.clear();
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        System.out.println("读取完成！");
    }

    /**
     * Netty各种事件处理
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof WebSocketClientProtocolHandler.ClientHandshakeStateEvent){
            WebSocketClientProtocolHandler.ClientHandshakeStateEvent event = (WebSocketClientProtocolHandler.ClientHandshakeStateEvent) evt;
            if(WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE == event){
                //发送初始化消息
                initHandler.sendInitMsg();
            }
        } else if(evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent) evt;
            if(event.state() == IdleState.WRITER_IDLE){
                //写空闲，发送心跳
                String heart = "{\"id\":\"ack\",\"name\":\"1\"}";
                ctx.writeAndFlush(heart);
            } else if(event.state() == IdleState.READER_IDLE){
                //读空闲，开始重连
                RunLogUtil.warn("未读取到服务器的消息，开始重连！");
                ctx.close();
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        RunLogUtil.error("Golang服务器连接异常！！！", cause);
        ctx.close();
    }

    /**
     * 记录消息日志
     */
    private void printMsgLog(JSONObject json, String reqId){

        //获得消息类型id
        String msgId = json.getString("id");

        if (includes != null && includes.size() > 0) {
            //如果有白名单，则只打印白名单
            if(!includes.contains(msgId)){
                //如果不存在, 直接结束
                return ;
            }
        } else if (excludes != null && excludes.size() > 0){
            //如果只有黑名单，则黑名单不打印
            if(excludes.contains(msgId)){
                //如果存在黑名单，直接结束
                return ;
            }
        }

        //打印日志
        RunLogUtil.warn("接收到Golang的消息 [ get msg ]：[" + json.getString("id") + "] --> " + json.toJSONString());
        //记录运行日志
        RunLogEntity runLogEntity = new RunLogEntity()
                .setRequestId(reqId)
                .setUrl("Get:接收到Golang的消息-" + json.getString("id") + " --> " + json.getString("id"))
                .setClassName(json.toJSONString())
                .setOrder(RequestIdThreadLocal.getNumber());
        RunLogUtil.runLog(rabbitTemplate, runLogEntity);
    }
}
